/*
 * completions.c
 */
#include <linux/completion.h>
#include <linux/err.h>		/* for IS_ERR() */
#include <linux/init.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/printk.h>
#include <linux/version.h>
#include <linux/delay.h>

static struct completion crank_comp;
static struct completion flywheel_comp;

static int machine_crank_thread(void *arg)
{
	msleep(5 * 1000);
	pr_info("Turn the crank\n");

	complete_all(&crank_comp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
	kthread_complete_and_exit(&crank_comp, 0);
#else
	complete_and_exit(&crank_comp, 0);
#endif
}

static int machine_flywheel_spinup_thread(void *arg)
{
	pr_info("wait completion crank_comp\n");
	wait_for_completion(&crank_comp);

	pr_info("Flywheel spins up\n");

	complete_all(&flywheel_comp);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
	kthread_complete_and_exit(&flywheel_comp, 0);
#else
	complete_and_exit(&flywheel_comp, 0);
#endif
}

static int __init completions_init(void)
{
	struct task_struct *crank_thread;
	struct task_struct *flywheel_thread;

	pr_info("completions example\n");

	init_completion(&crank_comp);
	init_completion(&flywheel_comp);

	crank_thread =
	    kthread_create(machine_crank_thread, NULL, "KThread Crank");
	if (IS_ERR(crank_thread))
		goto ERROR_THREAD_1;

	flywheel_thread = kthread_create(machine_flywheel_spinup_thread, NULL,
					 "KThread Flywheel");
	if (IS_ERR(flywheel_thread))
		goto ERROR_THREAD_2;

	wake_up_process(flywheel_thread);
	wake_up_process(crank_thread);

	return 0;

 ERROR_THREAD_2:
	kthread_stop(crank_thread);
 ERROR_THREAD_1:

	return -1;
}

static void __exit completions_exit(void)
{
	wait_for_completion(&crank_comp);
	wait_for_completion(&flywheel_comp);

	pr_info("completions exit\n");
}

module_init(completions_init);
module_exit(completions_exit);

MODULE_DESCRIPTION("Completions example");
MODULE_LICENSE("GPL");

/* 测试:
Dec 30 02:03:42 ubuntu kernel: [11528.560482] completions example
Dec 30 02:03:42 ubuntu kernel: [11528.560681] wait completion crank_comp
Dec 30 02:03:48 ubuntu kernel: [11533.773810] Turn the crank
Dec 30 02:03:48 ubuntu kernel: [11533.773917] Flywheel spins up

结论: Turn the crank相差5s, 符合预期
completion_all() 唤醒, wait_for_completion() 等待完成
 */
